<?php

namespace App\Library\Service;

use App\Library\Logic\AuctionBidLogic;
use App\Library\Logic\AuctionCertificationLogic;
use App\Library\Logic\AuctionFollowLogic;
use App\Library\Logic\AuctionLogic;
use App\Library\Logic\AuctionOrderLogic;
use App\Library\Logic\FollowLogic;
use App\Library\Logic\OpenIdLogic;
use App\Library\Logic\OperateLogLogic;
use App\Library\Logic\ShopUserAddressLogic;
use App\Library\Requests\Auction\ApplyCertificationRequest;
use App\Library\Requests\Auction\AuctionAddRequest;
use App\Library\Requests\Auction\AuctionAssignBidRequest;
use App\Library\Requests\Auction\AuctionBidPageRequest;
use App\Library\Requests\Auction\AuctionBidRequest;
use App\Library\Requests\Auction\AuctionCertificationEditRequest;
use App\Library\Requests\Auction\AuctionEditRequest;
use App\Library\Requests\Auction\AuctionIndexPageRequest;
use App\Library\Requests\Auction\AuctionPageRequest;
use App\Library\Requests\Base\BasePageRequest;
use App\Library\Requests\Base\SerialNumberRequest;
use App\Models\AuctionBidModel;
use App\Models\FollowModel;
use App\Models\OpenIdModel;
use DateTime;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

class AuctionService extends BaseService
{
    /**
     * @param AuctionLogic $logic
     */
    public function __construct(AuctionLogic $logic)
    {
        $this->logic = $logic;
    }

    /**
     * indexPaging
     * @param AuctionIndexPageRequest $request
     * @return JsonResponse
     */
    public static function indexPaging(AuctionIndexPageRequest $request): JsonResponse
    {
        return success(AuctionLogic::indexPaging($request));
    }

    /**
     * 出价记录
     * @param AuctionBidPageRequest $request
     * @return JsonResponse
     */
    public static function bidPaging(AuctionBidPageRequest $request): JsonResponse
    {
        $auction = self::newAuction($request->serialNumber, 'id');
        if (is_null($auction)) return fail();
        return success(AuctionLogic::bidPaging($request, $auction->id));
    }

    /**
     * 分页
     * @param AuctionPageRequest $request
     * @return JsonResponse
     */
    public function paging(AuctionPageRequest $request): JsonResponse
    {
        return success($this->logic->paging($request));
    }

    /**
     * 添加
     * @param AuctionAddRequest $request
     * @return JsonResponse
     */
    public function add(AuctionAddRequest $request): JsonResponse
    {
        if (!AuctionCertificationLogic::check()) return fail(611);
        $this->logic->add($request);
        OperateLogLogic::add();
        return success();
    }

    /**
     * 编辑
     * @param AuctionEditRequest $request
     * @return JsonResponse
     */
    public function edit(AuctionEditRequest $request): JsonResponse
    {
        $item = $this->logic->findBySerialNumber($request);
        if (is_null($item) || $item->publish_id !== USERID) return fail(433);
        else if ($item->status === 1) return failMessage('请先下架');
        $this->logic->edit($request, $item);
        OperateLogLogic::add();
        return success();
    }

    /**
     * 上下架
     * @param SerialNumberRequest $request
     * @return JsonResponse
     */
    public function editStatus(SerialNumberRequest $request): JsonResponse
    {
        $item = $this->logic->findBySerialNumber($request, ['id', 'status', 'publish_id']);
        if (is_null($item) || $item->publish_id !== USERID) return fail(433);
        $item->status = !$item->status;
        if ($item->status) $item->audit_status = 0;
        $item->save();
        OperateLogLogic::add();
        return success();
    }

    /**
     * 详情
     * @param SerialNumberRequest $request
     * @return JsonResponse
     */
    public function detail(SerialNumberRequest $request): JsonResponse
    {
        $auction = $this->logic->findBySerialNumber($request);
        if (is_null($auction)) return fail(433);
        $this->logic->getMedia($auction);
        $auction->address;
        $auction->storeInfo;
        return success($auction);
    }

    /**
     * 详情
     * @param SerialNumberRequest $request
     * @return JsonResponse
     */
    public static function indexDetail(SerialNumberRequest $request): JsonResponse
    {
        $auction = self::newAuction($request->serialNumber);
        if (is_null($auction)) return failMessage('当前暂无拍品，敬请期待');
        AuctionLogic::getMedia($auction);
        $userId = userId();
        $auction->author = $auction->publish_id === $userId;
        $auction->currentPrice = $auction->bid->price ?? $auction->start_price;
        $auction->bailStatus = !is_null($auction->bail);
        $auction->is_bid = AuctionBidLogic::existsByWhere([
            'auction_id' => $auction->id,
            'user_id' => $userId
        ]);
        $auction->follow;
        $auction->follow_person = AuctionFollowLogic::query()->where('auction_id', $auction->id)->count();
        $auction->storeInfo;
        $auction->followStore = FollowLogic::existsByWhere(['sellerId' => $auction->publish_id, 'userId' => $userId]);
        if ($auction->sell_out === 1) {
            $auction->orderStatus = AuctionOrderLogic::valueByWhere('auctionId', $auction->id, 'status');
        }
        unsetObject($auction, ['id', 'bail', 'bid']);
        return success($auction);
    }

    /**
     * certification
     * @return JsonResponse
     */
    public static function certification(): JsonResponse
    {
        return success(AuctionCertificationLogic::query()->with('refundAddress')->where([
            'user_id' => USERID,
            'status' => 1
        ])->first());
    }

    /**
     * 删除
     * @param SerialNumberRequest $request
     * @return JsonResponse
     */
    public function delete(SerialNumberRequest $request): JsonResponse
    {
        $item = $this->logic->findBySerialNumber($request, ['id', 'publish_id']);
        if (is_null($item) || $item->publish_id !== USERID) return fail(433);
        else if ($item->status === 1) return failMessage('请先下架');
        $item->delete();
        OperateLogLogic::add();
        return success();
    }

    /**
     * 出价
     * @param AuctionBidRequest $request
     * @return JsonResponse
     */
    public function bid(AuctionBidRequest $request): JsonResponse
    {
        $num = aesDecrypt($request->num);
        if ($num === false) return fail();
        $auction = $this->logic->findBySerialNumber($request);
        $dateTime = new DateTime($auction['end_at']);
        $dateTime->modify('-3 minutes');
        $newTimestamp = $dateTime->getTimestamp();
        $check = $this->checkBid($auction);
        if (is_int($check)) return fail($check);
        $dbNum = $auction->bid->num ?? 0;
        if ((int)$num !== $dbNum) return fail(602);
        $price = is_null($auction->bid) ? $auction->start_price : (float)bcadd($auction->bid->price, $auction->inc, 2);
        if (!is_null($auction->capping_price) && $auction->capping_price > 0 && $price > $auction->capping_price) {
            return fail(641);
        }
        $bidInfo = AuctionBidLogic::query()->where([
            'auction_id' => $auction->id,
        ])->orderBy('created_at', 'desc')->first();
        if (!empty($bidInfo)) {
            $userInfo = OpenIdLogic::query()->where([
                'user_id' => $bidInfo['user_id'],
                'type' => 'wx',
            ])->first();
            WxMessageService::auctionWxMess($auction, $userInfo, 3);
        }
        AuctionBidLogic::insertForModel(['auctionId' => $auction->id, 'userId' => USERID, 'num' => $dbNum + 1, 'price' => $price]);
        $endAt = $auction['end_at'];
        if (time() < strtotime($auction['end_at']) && time() > $newTimestamp) {
            $endAt = Carbon::now()->addMinutes(3);
        }
        AuctionLogic::updateForModel([
            'currentPrice' => $price,
            'shotCount' => $dbNum + 1,
            'userCount' => AuctionBidLogic::userCount($auction->id),
            'end_at' => $endAt
        ], $auction);
        cache()->increment('bid:' . $request->serialNumber);
        cache(['bidHigher:' . $request->serialNumber => USERID]);
       /* $userInfo = OpenIdModel::query()->where([
            'user_id' => USERID,
            'type' => 'wx'
        ])->first();
        if (!empty($userInfo)) {
            $auction->current_price = $price;
            WxMessageService::auctionWxMess($auction, $userInfo);
        }*/

        return success();
    }

    /**
     * 参拍记录
     * @param BasePageRequest $request
     * @return JsonResponse
     */
    public function bidRecord(BasePageRequest $request): JsonResponse
    {
        $pageData = AuctionBidLogic::bidRecord($request);
        foreach ($pageData['data'] as &$value) {
            if ($value->user_id === (int)redis('get', 'bidHigher:' . $value->serial_number)) $value->is_bid_higher = 1;
            else $value->is_bid_higher = 0;
        }
        return success($pageData);
    }

    /**
     * checkBail
     * @param SerialNumberRequest $request
     * @return JsonResponse
     */
    public function checkBail(SerialNumberRequest $request): JsonResponse
    {
        return success($this->logic->checkBail($request));
    }

    /**
     * applyCertification
     * @param ApplyCertificationRequest $request
     * @return JsonResponse
     */
    public function applyCertification(ApplyCertificationRequest $request): JsonResponse
    {
        $status = AuctionCertificationLogic::findStatusByUserId();
        if (!is_null($status)) {
            if ($status === 0) return fail(609);
            else if ($status === 1) return fail(610);
        }
        $request->userId = USERID;
        AuctionCertificationLogic::insertForModel(handleRequest($request));
        OperateLogLogic::add();
        return success();
    }

    /**
     * 拍卖商品关注或取消
     * @param SerialNumberRequest $request
     * @return JsonResponse
     */
    public function followOrCancel(SerialNumberRequest $request): JsonResponse
    {
        //判断拍卖商品是否存在
        $auction = $this->logic->findBySerialNumber($request);
        if (is_null($auction)) return fail(433);
        if ($auction->expired === 1) return fail(607);
        AuctionFollowLogic::followOrCancel($auction);
        return success();
    }

    /**
     * 校验发布人资格
     * @return JsonResponse
     */
    public function publishCertification(): JsonResponse
    {
        return success(AuctionCertificationLogic::findStatusByUserId());
    }

    /**
     * numTj
     * @return JsonResponse
     */
    public function numTj(): JsonResponse
    {
        return success($this->logic->numTj());
    }

    /**
     * certificationEdit
     * @param AuctionCertificationEditRequest $request
     * @return JsonResponse
     *
     */
    public function certificationEdit(AuctionCertificationEditRequest $request): JsonResponse
    {
        $item = AuctionCertificationLogic::query()->where('user_id', USERID)->first();
        if (is_null($item)) {
            AuctionCertificationLogic::insertForModel([
                'user_id' => USERID,
                'store_name' => is_null($request->post('storeName')) ? '' : $request->post('storeName'),
                'store_avatar_file_id' => is_null($request->post('storeAvatarFileId')) ? '' : $request->post('storeAvatarFileId'),
            ]);
        } else {
            $item->store_name = is_null($request->post('storeName')) ? '' : $request->post('storeName');
            $item->store_avatar_file_id = is_null($request->post('storeAvatarFileId')) ? '' : $request->post('storeAvatarFileId');
            $item->save();
        }
        if (!is_null($request->post('addressId'))) {
            $oldRefund = ShopUserAddressLogic::query()->where(['user_id' => USERID, 'ua_is_refund' => 1])->first();
            if (!is_null($oldRefund)) {
                $oldRefund->ua_is_refund = 0;
                $oldRefund->save();
            }
            ShopUserAddressLogic::query()->where([
                'ua_id' => $request->post('addressId')
            ])->update(['ua_is_refund' => 1]);
        }
        OperateLogLogic::add();
        return success();
    }

    /**
     * followPaging
     * @param BasePageRequest $request
     * @return JsonResponse
     */
    public function followPaging(BasePageRequest $request): JsonResponse
    {
        return success(AuctionFollowLogic::paging($request));
    }

    /**
     * 出价
     * @param AuctionBidRequest $request
     * @return JsonResponse
     * @throws Exception
     */
    public function buy(SerialNumberRequest $request): JsonResponse
    {
        $auction = $this->logic->findBySerialNumber($request);

        $check = $this->checkBid($auction);

        if (is_int($check)) return fail($check);
        else if (is_null($auction->capping_price) || $auction->capping_price == 0) {
            return failMessage('未设置一口价');
        }
        $dbNum = $auction->bid->num ?? 0;
        AuctionBidLogic::insert([
            'auctionId' => $auction->id,
            'userId' => USERID,
            'num' => $dbNum + 1,
            'price' => $auction->capping_price,
            'isLast' => 1,
        ]);
        AuctionLogic::updateForModel([
            'sellOut' => 1,
            'isEnd' => 1,
            'shotCount' => $dbNum + 1,
            'userCount' => AuctionBidLogic::userCount($auction->id),
            'currentPrice' => $auction->capping_price,
        ], $auction);
        $auctionOrder = AuctionOrderLogic::insertForModel([
            'auctionId' => $auction->id,
            'fileId' => $auction->file_id,
            'title' => $auction->title,
            'userId' => USERID,
            'sellerId' => $auction->publish_id,
            'payPrice' => bcadd($auction->capping_price, $auction->freight, 2),
            'orderPrice' => $auction->capping_price,
            'freight' => $auction->freight,
            'payEndAt' => now()->addDays(3),
        ]);
        mq('auctionPayEnd', $auctionOrder, $auctionOrder->pay_end_at);
        mq('AuctionBailRefundAllCommand', $auction);
        return success($auctionOrder->serial_number);
    }

    /**
     * 指定价格出价
     * @param AuctionAssignBidRequest $request
     * @return JsonResponse
     */
    public function assign(AuctionAssignBidRequest $request): JsonResponse
    {
        $auction = $this->logic->findBySerialNumber($request);
        $check = $this->checkBid($auction);
        if (is_int($check)) return fail($check);
        $dbNum = $auction->bid->num ?? 0;
        $price = is_null($auction->bid) ? $auction->start_price : (float)bcadd($auction->bid->price, $auction->inc, 2);
        if ((float)$request->price < $price) {
            return failMessage('当前最少出价:' . $price);
        } else if (!is_null($auction->capping_price) && $auction->capping_price > 0 && $request->price > $auction->capping_price) {
            return fail(641);
        }
        AuctionBidLogic::insert(['auctionId' => $auction->id, 'userId' => USERID, 'num' => $dbNum + 1, 'price' => $request->price]);
        AuctionLogic::updateForModel([
            'shotCount' => $dbNum + 1,
            'userCount' => AuctionBidLogic::userCount($auction->id),
            'currentPrice' => $request->price,
        ], $auction);
        cache()->increment('bid:' . $request->serialNumber);
        cache(['bidHigher:' . $request->serialNumber => USERID]);
        return success();
    }

    /**
     * 复制
     * @param SerialNumberRequest $request
     * @return JsonResponse
     */
    public function copy(SerialNumberRequest $request): JsonResponse
    {
        $auction = $this->logic->findBySerialNumber($request);
        if (is_null($auction) || $auction->publish_id !== USERID) return fail(433);
        OperateLogLogic::add();
        return success($this->logic->copy($auction));
    }

    /**
     *
     * Author: zlf
     * Created at 2024/11/27 17:00
     * @param $request
     * @return JsonResponse
     */
    public function report($request): JsonResponse
    {
        $auction = $this->logic->findBySerialNumber($request);
        if (is_null($auction)) return failMessage('无拍卖商品信息');
        if (!preg_match('/^1[3-9]\d{9}$/', $request->mobile)) return failMessage('手机号格式有误');
        DB::table('auction_report')->insertGetId([
            'user_id' => USERID,
            'auction_id' => $auction->id,
            'mobile' => $request->mobile,
            'content' => $request->content
        ]);
        OperateLogLogic::add();
        return success();
    }

    private function checkBid(?Model $auction): ?int
    {
        if (is_null($auction) || USERID === 0) return 433;
        else if ($auction->publish_id === USERID) return 605;
        else if ($auction->status === 0) return -1;
        else if ($auction->audit_status !== 1) return -1;
        else if ($auction->sell_out === 1) return 606;
        else if ($auction->expired === 1) return 607;
        //else if (now()->lt(now()->parse($auction->start_at))) return 603;
        else if (now()->gt(now()->parse($auction->end_at))) return 604;
        else if ($auction->bail_price > 0 && is_null($auction->bail)) return 608;
        //else if (!is_null($auction->bid) && $auction->bid->user_id === USERID) return 601;
        return null;
    }

    private static function newAuction(string $serialNumber, array|string $columns = ['*']): ?Model
    {
        if ($serialNumber === '00000000000000000000') {
            return AuctionLogic::query()
                ->orderBy('start')
                ->where([
                    'audit_status' => 1,
                    'status' => 1,
                    'is_end' => 0,

                ])->whereIn('is_start', [0, 1])
                ->orderByDesc('recommend')->orderBy('sort')
                ->orderByDesc('created_at')
                ->first(array_merge(is_string($columns) ? [$columns] : $columns, [
                    DB::raw('UNIX_TIMESTAMP()-unix_timestamp(start_at) as start'),
                ]));
        } else return AuctionLogic::findBySerialNumber($serialNumber, $columns);
    }
}
